﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using Medianamik.Core.Logging;
using Medianamik.Core.Sugar;

namespace Medianamik.Core.Action
{
    public class ActionManager
    {
        private readonly List<IActionHandler> _actionHandlers = new List<IActionHandler>();

        private ILogger _logger;
        private ILogger Logger
        {
            get
            {
                return _logger
                    ?? (_logger = new ObjectFactory<ILogger>().Get("ActionManagerLogger"));
            }
        }

         /// <summary>
        /// Permet de consulter les ActionHandlers actuellement chargés
        /// </summary>
        public Dictionary<string, string> ActionHandlers
        {
            get
            {
                return _actionHandlers.ToDictionary(ia => ia.GetType().FullName, ia => ia.Description);
            }
        }

        public static void RegisterActionHandler(string pAssemblyQualifiedActionHandlerName) 
        {
            var t = Type.GetType(pAssemblyQualifiedActionHandlerName, true);

            if (t != null)
            {
                var typeInstance = Activator.CreateInstance(t) as IActionHandler;
                if (typeInstance == null) { return;}

                typeInstance.Initialize(Instance);
                Instance._actionHandlers.Add(typeInstance);
            }
        }

        public static void RegisterActionHandler<T>() where T: IActionHandler, new()
        {
            IActionHandler typeInstance = new T();
            typeInstance.Initialize(Instance);
            Instance._actionHandlers.Add(typeInstance);
        }

        public static ActionManager Instance
        {
            get
            {
                return Singleton<ActionManager>.Instance;
            }
        }

        private EventHandlerList _events;
        protected EventHandlerList Events
        {
            get
            {
                return _events ?? (_events = new EventHandlerList());
            }
        }

        #region Authentication Events

        private static readonly object AuthenticationRequestedEvent = new object();
        public event Action<ActionEventArgs<object>> AuthenticationRequested
        {
            add
            {
                Events.AddHandler(AuthenticationRequestedEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AuthenticationRequestedEvent, value);
            }
        }

        internal void OnAuthenticationRequested(ActionEventArgs<object> args)
        {
            HandleEvent(args, AuthenticationRequestedEvent);
        }

        #endregion

        #region Node Events

        #region Create

        private static readonly object BeforeCreateNodeEvent = new object();
        public event Action<ActionEventArgs<Node>> BeforeCreateNode
        {
            add
            {
                Events.AddHandler(BeforeCreateNodeEvent, value);
            }
            remove
            {
                Events.RemoveHandler(BeforeCreateNodeEvent, value);
            }
        }

        private static readonly object AfterCreateNodeEvent = new object();
        public event Action<ActionEventArgs<Node>> AfterCreateNode
        {
            add
            {
                Events.AddHandler(AfterCreateNodeEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterCreateNodeEvent, value);
            }
        }

        internal void OnBeforeCreateNode(ActionEventArgs<Node> args)
        {
            HandleEvent(args, BeforeCreateNodeEvent);
        }

        internal void OnAfterCreateNode(ActionEventArgs<Node> args)
        {
            HandleEvent(args, AfterCreateNodeEvent);
        }

        #endregion

        #region Update

        private static readonly object BeforeUpdateNodeEvent = new object();
        public event Action<ActionEventArgs<Node>> BeforeUpdateNode
        {
            add
            {
                Events.AddHandler(BeforeUpdateNodeEvent, value);
            }
            remove
            {
                Events.RemoveHandler(BeforeUpdateNodeEvent, value);
            }
        }

        private static readonly object AfterUpdateNodeEvent = new object();
        public event Action<ActionEventArgs<Node>> AfterUpdateNode
        {
            add
            {
                Events.AddHandler(AfterUpdateNodeEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterUpdateNodeEvent, value);
            }
        }

        internal void OnBeforeUpdateNode(ActionEventArgs<Node> args)
        {
            HandleEvent(args, BeforeUpdateNodeEvent);
        }

        internal void OnAfterUpdateNode(ActionEventArgs<Node> args)
        {
            HandleEvent(args, AfterUpdateNodeEvent);
        }

        #endregion

        #region Delete

        private static readonly object BeforeDeleteNodeEvent = new object();
        public event Action<ActionEventArgs<Node>> BeforeDeleteNode
        {
            add
            {
                Events.AddHandler(BeforeDeleteNodeEvent, value);
            }
            remove
            {
                Events.RemoveHandler(BeforeDeleteNodeEvent, value);
            }
        }

        private static readonly object AfterDeleteNodeEvent = new object();
        public event Action<ActionEventArgs<Node>> AfterDeleteNode
        {
            add
            {
                Events.AddHandler(AfterDeleteNodeEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterDeleteNodeEvent, value);
            }
        }

        internal void OnBeforeDeleteNode(ActionEventArgs<Node> args)
        {
            HandleEvent(args, BeforeDeleteNodeEvent);
        }

        internal void OnAfterDeleteNode(ActionEventArgs<Node> args)
        {
            HandleEvent(args, AfterDeleteNodeEvent);
        }

        #endregion

        #region Move

        private static readonly object AfterMoveNodeEvent = new object();
        public event Action<NodeMovedEventArgs> AfterMoveNode
        {
            add
            {
                Events.AddHandler(AfterMoveNodeEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterMoveNodeEvent, value);
            }
        }

        internal void OnAfterMoveNode(NodeMovedEventArgs args)
        {
            HandleEvent(args, AfterMoveNodeEvent);
        }

        #endregion

        #endregion

        #region NodeInstance Events

        #region  Create

        private static readonly object BeforeCreateNodeInstanceEvent = new object();
        public event Action<ActionEventArgs<NodeInstance>> BeforeCreateNodeInstance
        {
            add
            {
                Events.AddHandler(BeforeCreateNodeInstanceEvent, value);
            }
            remove
            {
                Events.RemoveHandler(BeforeCreateNodeInstanceEvent, value);
            }
        }

        private static readonly object AfterCreateNodeInstanceEvent = new object();
        public event Action<ActionEventArgs<NodeInstance>> AfterCreateNodeInstance
        {
            add
            {
                Events.AddHandler(AfterCreateNodeInstanceEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterCreateNodeInstanceEvent, value);
            }
        }

        internal void OnBeforeCreateNodeInstance(ActionEventArgs<NodeInstance> args)
        {
            HandleEvent(args, BeforeCreateNodeInstanceEvent);
        }

        internal void OnAfterCreateNodeInstance(ActionEventArgs<NodeInstance> args)
        {
            HandleEvent(args, AfterCreateNodeInstanceEvent);
        }

        #endregion

        #region Update

        private static readonly object BeforeUpdateNodeInstanceEvent = new object();
        public event Action<ActionEventArgs<NodeInstance>> BeforeUpdateNodeInstance
        {
            add
            {
                Events.AddHandler(BeforeUpdateNodeInstanceEvent, value);
            }
            remove
            {
                Events.RemoveHandler(BeforeUpdateNodeInstanceEvent, value);
            }
        }

        private static readonly object AfterUpdateNodeInstanceEvent = new object();
        public event Action<ActionEventArgs<NodeInstance>> AfterUpdateNodeInstance
        {
            add
            {
                Events.AddHandler(AfterUpdateNodeInstanceEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterUpdateNodeInstanceEvent, value);
            }
        }

        internal void OnBeforeUpdateNodeInstance(ActionEventArgs<NodeInstance> args)
        {
            HandleEvent(args, BeforeUpdateNodeInstanceEvent);
        }

        internal void OnAfterUpdateNodeInstance(ActionEventArgs<NodeInstance> args)
        {
            HandleEvent(args, AfterUpdateNodeInstanceEvent);
        }

        #endregion

        #region Delete

        private static readonly object BeforeDeleteNodeInstanceEvent = new object();
        public event Action<ActionEventArgs<NodeInstance>> BeforeDeleteNodeInstance
        {
            add
            {
                Events.AddHandler(BeforeDeleteNodeInstanceEvent, value);
            }
            remove
            {
                Events.RemoveHandler(BeforeDeleteNodeInstanceEvent, value);
            }
        }

        private static readonly object AfterDeleteNodeInstanceEvent = new object();
        public event Action<ActionEventArgs<NodeInstance>> AfterDeleteNodeInstance
        {
            add
            {
                Events.AddHandler(AfterDeleteNodeInstanceEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterDeleteNodeInstanceEvent, value);
            }
        }

        internal void OnBeforeDeleteNodeInstance(ActionEventArgs<NodeInstance> args)
        {
            HandleEvent(args, BeforeDeleteNodeInstanceEvent);
        }

        internal void OnAfterDeleteNodeInstance(ActionEventArgs<NodeInstance> args)
        {
            HandleEvent(args, AfterDeleteNodeInstanceEvent);
        }

        #endregion

        #region Publish

        private static readonly object BeforePublishNodeInstanceEvent = new object();
        public event Action<ActionEventArgs<NodeInstance>> BeforePublishNodeInstance
        {
            add
            {
                Events.AddHandler(BeforePublishNodeInstanceEvent, value);
            }
            remove
            {
                Events.RemoveHandler(BeforePublishNodeInstanceEvent, value);
            }
        }

        private static readonly object AfterPublishNodeInstanceEvent = new object();
        public event Action<ActionEventArgs<NodeInstance>> AfterPublishNodeInstance
        {
            add
            {
                Events.AddHandler(AfterPublishNodeInstanceEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterPublishNodeInstanceEvent, value);
            }
        }

        internal void OnBeforePublishNodeInstance(ActionEventArgs<NodeInstance> args)
        {
            HandleEvent(args, BeforePublishNodeInstanceEvent);
        }

        internal void OnAfterPublishNodeInstance(ActionEventArgs<NodeInstance> args)
        {
            HandleEvent(args, AfterPublishNodeInstanceEvent);
        }

        #endregion

        #region Approve

        private static readonly object BeforeApproveNodeInstanceEvent = new object();
        public event Action<ActionEventArgs<NodeInstance>> BeforeApproveNodeInstance
        {
            add
            {
                Events.AddHandler(BeforeApproveNodeInstanceEvent, value);
            }
            remove
            {
                Events.AddHandler(BeforeApproveNodeInstanceEvent, value);
            }
        }

        private static readonly object AfterApproveNodeInstanceEvent = new object();
        public event Action<ActionEventArgs<NodeInstance>> AfterApproveNodeInstance
        {
            add
            {
                Events.AddHandler(AfterApproveNodeInstanceEvent, value);
            }
            remove
            {
                Events.AddHandler(AfterApproveNodeInstanceEvent, value);
            }
        }

        internal void OnBeforeApproveNodeInstance(ActionEventArgs<NodeInstance> args)
        {
            HandleEvent(args, BeforeApproveNodeInstanceEvent);
        }

        internal void OnAfterApproveNodeInstance(ActionEventArgs<NodeInstance> args)
        {
            HandleEvent(args, AfterApproveNodeInstanceEvent);
        }

        #endregion

        #region Revert

        //private static readonly object _beforeRevertNodeInstanceEvent = new object();

        private static readonly object AfterRevertNodeInstanceEvent = new object();
        public event Action<ActionEventArgs<NodeInstance>> AfterRevertNodeInstance
        {
            add
            {
                Events.AddHandler(AfterRevertNodeInstanceEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterRevertNodeInstanceEvent, value);
            }
        }

        internal void OnAfterRevertNodeInstance(ActionEventArgs<NodeInstance> args)
        {
            HandleEvent(args, AfterRevertNodeInstanceEvent);
        }

        #endregion

        #region Status

        private static readonly object BeforeChangeNodeInstanceActiveStatusEvent = new object();
        public event Action<ActionEventArgs<NodeInstance>> BeforeChangeNodeInstanceActiveStatus
        {
            add
            {
                Events.AddHandler(BeforeChangeNodeInstanceActiveStatusEvent, value);
            }
            remove
            {
                Events.RemoveHandler(BeforeChangeNodeInstanceActiveStatusEvent, value);
            }
        }

        private static readonly object AfterChangeNodeInstanceActiveStatusEvent = new object();
        public event Action<ActionEventArgs<NodeInstance>> AfterChangeNodeInstanceActiveStatus
        {
            add
            {
                Events.AddHandler(AfterChangeNodeInstanceActiveStatusEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterChangeNodeInstanceActiveStatusEvent, value);
            }
        }

        internal void OnBeforeChangeNodeInstanceActiveStatus(ActionEventArgs<NodeInstance> args)
        {
            HandleEvent(args, BeforeChangeNodeInstanceActiveStatusEvent);
        }

        internal void OnAfterChangeNodeInstanceActiveStatus(ActionEventArgs<NodeInstance> args)
        {
            HandleEvent(args, AfterChangeNodeInstanceActiveStatusEvent);
        }

        #endregion

        #endregion

        #region NodeType Events

        #region Create

        private static readonly object BeforeCreateNodeTypeEvent = new object();
        public event Action<ActionEventArgs<NodeType>> BeforeCreateNodeType
        {
            add
            {
                Events.AddHandler(BeforeCreateNodeTypeEvent, value);
            }
            remove
            {
                Events.RemoveHandler(BeforeCreateNodeTypeEvent, value);
            }
        }

        private static readonly object AfterCreateNodeTypeEvent = new object();
        public event Action<ActionEventArgs<NodeType>> AfterCreateNodeType
        {
            add
            {
                Events.AddHandler(AfterCreateNodeTypeEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterCreateNodeTypeEvent, value);
            }
        }

        internal void OnBeforeCreateNodeType(ActionEventArgs<NodeType> args)
        {
            HandleEvent(args, BeforeCreateNodeTypeEvent);
        }
        internal void OnAfterCreateNodeType(ActionEventArgs<NodeType> args)
        {
            HandleEvent(args, AfterCreateNodeTypeEvent);
        }

        #endregion

        #region Update

        private static readonly object BeforeUpdateNodeTypeEvent = new object();
        public event Action<ActionEventArgs<NodeType>> BeforeUpdateNodeType
        {
            add
            {
                Events.AddHandler(BeforeUpdateNodeTypeEvent, value);
            }
            remove
            {
                Events.RemoveHandler(BeforeUpdateNodeTypeEvent, value);
            }
        }

        private static readonly object AfterUpdateNodeTypeEvent = new object();
        public event Action<ActionEventArgs<NodeType>> AfterUpdateNodeType
        {
            add
            {
                Events.AddHandler(AfterUpdateNodeTypeEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterUpdateNodeTypeEvent, value);
            }
        }

        internal void OnBeforeUpdateNodeType(ActionEventArgs<NodeType> args)
        {
            HandleEvent(args, BeforeUpdateNodeTypeEvent);
        }
        internal void OnAfterUpdateNodeType(ActionEventArgs<NodeType> args)
        {
            HandleEvent(args, AfterUpdateNodeTypeEvent);
        }

        #endregion

        #region Delete

        private static readonly object BeforeDeleteNodeTypeEvent = new object();
        public event Action<ActionEventArgs<NodeType>> BeforeDeleteNodeType
        {
            add
            {
                Events.AddHandler(BeforeDeleteNodeTypeEvent, value);
            }
            remove
            {
                Events.RemoveHandler(BeforeDeleteNodeTypeEvent, value);
            }
        }

        private static readonly object AfterDeleteNodeTypeEvent = new object();
        public event Action<ActionEventArgs<NodeType>> AfterDeleteNodeType
        {
            add
            {
                Events.AddHandler(AfterDeleteNodeTypeEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterDeleteNodeTypeEvent, value);
            }
        }

        internal void OnBeforeDeleteNodeType(ActionEventArgs<NodeType> args)
        {
            HandleEvent(args, BeforeDeleteNodeTypeEvent);
        }

        internal void OnAfterDeleteNodeType(ActionEventArgs<NodeType> args)
        {
            HandleEvent(args, AfterDeleteNodeTypeEvent);
        }

        #endregion

        #endregion

        #region NodeTypeProperty Events

        #region Create

        private static readonly object BeforeCreateNodeTypePropertyEvent = new object();
        public event Action<ActionEventArgs<NodeTypeProperty>> BeforeCreateNodeTypeProperty
        {
            add
            {
                Events.AddHandler(BeforeCreateNodeTypePropertyEvent, value);
            }
            remove
            {
                Events.RemoveHandler(BeforeCreateNodeTypePropertyEvent, value);
            }
        }

        //private static readonly object _afterCreateNodeTypePropertyEvent = new object();
        //public event Action<ActionEventArgs<NodeTypeProperty>> AfterCreateNodeTypeProperty
        //{
        //    add
        //    {
        //        Events.AddHandler(_afterCreateNodeTypePropertyEvent, value);
        //    }
        //    remove
        //    {
        //        Events.RemoveHandler(_afterCreateNodeTypePropertyEvent, value);
        //    }
        //}

        private static readonly object AfterCreateNodeTypePropertiesEvent = new object();
        public event Action<ActionEventArgs<IEnumerable<NodeTypeProperty>>> AfterCreateNodeTypeProperties
        {
            add
            {
                Events.AddHandler(AfterCreateNodeTypePropertiesEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterCreateNodeTypePropertiesEvent, value);
            }
        }

        internal void OnBeforeCreateNodeTypeProperty(ActionEventArgs<NodeTypeProperty> args)
        {
            HandleEvent(args, BeforeCreateNodeTypePropertyEvent);
        }
        //internal void OnAfterCreateNodeTypeProperty(ActionEventArgs<NodeTypeProperty> args)
        //{
        //    HandleEvent(args, _afterCreateNodeTypePropertyEvent);
        //}

        internal void OnAfterCreateNodeTypeProperties(ActionEventArgs<IEnumerable<NodeTypeProperty>> args)
        {
            HandleEvent(args, AfterCreateNodeTypePropertiesEvent);
        }

        #endregion

        #region Update

        private static readonly object BeforeUpdateNodeTypePropertyEvent = new object();
        public event Action<ActionEventArgs<NodeTypeProperty>> BeforeUpdateNodeTypeProperty
        {
            add
            {
                Events.AddHandler(BeforeUpdateNodeTypePropertyEvent, value);
            }
            remove
            {
                Events.RemoveHandler(BeforeUpdateNodeTypePropertyEvent, value);
            }
        }

        private static readonly object AfterUpdateNodeTypePropertyEvent = new object();
        public event Action<ActionEventArgs<NodeTypeProperty>> AfterUpdateNodeTypeProperty
        {
            add
            {
                Events.AddHandler(AfterUpdateNodeTypePropertyEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterUpdateNodeTypePropertyEvent, value);
            }
        }

        internal void OnBeforeUpdateNodeTypeProperty(ActionEventArgs<NodeTypeProperty> args)
        {
            HandleEvent(args, BeforeUpdateNodeTypePropertyEvent);
        }
        internal void OnAfterUpdateNodeTypeProperty(ActionEventArgs<NodeTypeProperty> args)
        {
            HandleEvent(args, AfterUpdateNodeTypePropertyEvent);
        }

        #endregion

        #region Delete

        private static readonly object BeforeDeleteNodeTypePropertyEvent = new object();
        internal void OnBeforeDeleteNodeTypeProperty(ActionEventArgs<NodeTypeProperty> args)
        {
            HandleEvent(args, BeforeDeleteNodeTypePropertyEvent);
        }

        private static readonly object AfterDeleteNodeTypePropertiesEvent = new object();
        internal void OnAfterDeleteNodeTypeProperties(ActionEventArgs<IEnumerable<NodeTypeProperty>> args)
        {
            HandleEvent(args, AfterDeleteNodeTypePropertiesEvent);
        }

        //private static readonly object _afterDeleteNodeTypePropertyEvent = new object();
        //internal void OnAfterDeleteNodeTypeProperty(ActionEventArgs<NodeTypeProperty> args)
        //{
        //    HandleEvent(args, _afterDeleteNodeTypePropertyEvent);
        //}

        //public event Action<ActionEventArgs<NodeTypeProperty>> BeforeDeleteNodeTypeProperty
        //{
        //    add
        //    {
        //        Events.AddHandler(_beforeDeleteNodeTypePropertyEvent, value);
        //    }
        //    remove
        //    {
        //        Events.RemoveHandler(_beforeDeleteNodeTypePropertyEvent, value);
        //    }
        //}

        public event Action<ActionEventArgs<IEnumerable<NodeTypeProperty>>> AfterDeleteNodeTypeProperties
        {
            add
            {
                Events.AddHandler(AfterDeleteNodeTypePropertiesEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterDeleteNodeTypePropertiesEvent, value);
            }
        }

        #endregion

        #endregion

        #region NodeProperty Events

        #region Create

        private static readonly object AfterCreateNodePropertyEvent = new object();
        public event Action<NodePropertyActionEventArgs> AfterCreateNodeProperty
        {
            add
            {
                Events.AddHandler(AfterCreateNodePropertyEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterCreateNodePropertyEvent, value);
            }
        }

        internal void OnAfterCreateNodeProperty(NodePropertyActionEventArgs args)
        {
            HandleEvent(args, AfterCreateNodePropertyEvent);
        }

        #endregion

        #region Update

        private static readonly object AfterUpdateNodePropertyEvent = new object();
        public event Action<NodePropertyActionEventArgs> AfterUpdateNodeProperty
        {
            add
            {
                Events.AddHandler(AfterUpdateNodePropertyEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterUpdateNodePropertyEvent, value);
            }
        }

        internal void OnAfterUpdateNodeProperty(NodePropertyActionEventArgs args)
        {
            HandleEvent(args, AfterUpdateNodePropertyEvent);
        }

        #endregion

        #endregion

        #region NodeTypeInterface Events

        #region Implement

        private static readonly object AfterImplementNodeTypeInterfacesEvent = new object();
        public event Action<ActionEventArgs<IEnumerable<NodeTypeInterfaceImplementation>>> AfterImplementNodeTypeInterfaces
        {
            add
            {
                Events.AddHandler(AfterImplementNodeTypeInterfacesEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterImplementNodeTypeInterfacesEvent, value);
            }
        }

        internal void OnAfterImplementNodeTypeInterfaces(ActionEventArgs<IEnumerable<NodeTypeInterfaceImplementation>> args)
        {
            HandleEvent(args, AfterImplementNodeTypeInterfacesEvent);
        }

        #endregion

        #region Unimplement

        private static readonly object AfterUnimplementNodeTypeInterfacesEvent = new object();
        public event Action<ActionEventArgs<IEnumerable<NodeTypeInterfaceImplementation>>> AfterUnimplementNodeTypeInterfaces
        {
            add
            {
                Events.AddHandler(AfterUnimplementNodeTypeInterfacesEvent, value);
            }
            remove
            {
                Events.RemoveHandler(AfterUnimplementNodeTypeInterfacesEvent, value);
            }
        }

        internal void OnAfterUnimplementNodeTypeInterfaces(ActionEventArgs<IEnumerable<NodeTypeInterfaceImplementation>> args)
        {
            HandleEvent(args, AfterUnimplementNodeTypeInterfacesEvent);
        }

        #endregion

        #endregion

        private void HandleEvent<T>(T args, object eventObj)
        {
            var eh = Events[eventObj] as Action<T>;

            if (eh != null)
            {
                var eventList = eh.GetInvocationList().Cast<Action<T>>().ToArray();
                foreach (var t in eventList)
                {
                    try
                    {
                        t(args);
                    }
                    catch (Exception e) { HandleException(e); }
                }
            }
        }

        private void HandleException(Exception e)
        {
           Logger.Warn("Erreur dans ActionManager : " + e.ToStringWithDetails());
        }
    }
}